/*
 * HttpParser.cpp
 *
 */

#include "HttpParser.h"
#include "HttpCodes.h"
#include "HttpGet.h"
#include "HttpPost.h"
#include "HttpStringBody.h"
#include "../../common/StrUtils.h"
#include <iostream>

ParserException::ParserException(const std::string& msg, int code) throw () :
	msg(msg), errCode(code) {
}

HttpParser::HttpParser() {}

HttpParser::~HttpParser() {}

std::string HttpParser::getProtocolName(const std::string& version) {
	size_t endSepProtocolName = version.find('/');
	if(endSepProtocolName != std::string::npos && endSepProtocolName > 0) {
		return version.substr(0, endSepProtocolName);
	}
	throw ParserException("Version de protocolo mal formado",
			HttpCodes::BAD_REQUEST);
}

std::string HttpParser::getMayorVersion(const std::string& version) {
	size_t begSepMayorVer = version.find('/');
	if(begSepMayorVer != std::string::npos) {
		size_t endSepMayorVer = version.find('.');
		if(endSepMayorVer != std::string::npos) {
			size_t longMayorVer = endSepMayorVer - begSepMayorVer - 1;
			if(longMayorVer > 0)
				return version.substr(begSepMayorVer + 1, longMayorVer);
		}
	}
	throw ParserException("Version de protocolo mal formado",
			HttpCodes::BAD_REQUEST);
}

HttpHeader* HttpParser::parseHeader(const std::string& line) {
	unsigned int nameEnd, valueBeg;
	unsigned int separator = line.find(HttpHeader::SEPARATOR);
	if (separator == std::string::npos || separator == 0)
		throw ParserException("Header mal formado", HttpCodes::BAD_REQUEST);
	nameEnd = line.find_first_of(HttpMessage::WHITES);
	nameEnd = (nameEnd > separator)? separator : nameEnd;
	valueBeg = line.find_first_not_of(HttpMessage::WHITES, separator + 1);
	std::string name = line.substr(0, nameEnd);
	std::string value = line.substr(valueBeg, line.length());
	return new HttpHeader(name, value);
}

HttpRequest* HttpParser::parseRequestLine(const std::string& line) {
	std::string method;
	std::string uri;
	std::string version;
	size_t firstSpace, lastSpace;
	firstSpace = line.find_first_of(HttpMessage::SP);
	lastSpace = line.find_last_of(HttpMessage::SP);
	method = line.substr(0, firstSpace);
	uri = line.substr(firstSpace + 1, lastSpace - 1 - firstSpace);
	version = line.substr(lastSpace + 1);
	HttpRequest* request = NULL;
	if (HttpParser::getProtocolName(version) == HttpHost::PROTOCOL_NAME &&
			HttpParser::getMayorVersion(version) == HttpHost::MAYOR_VERSION) {
		if (method == HttpRequest::GET)
			request = new HttpGet(uri, version);
		else if (method == HttpRequest::POST)
			request = new HttpPost(uri, version);
		else
			throw ParserException("Método no reconocido",
					HttpCodes::NOT_IMPLEMENTED);
	}
	else
		throw ParserException("Versión de protocolo no aceptada",
				HttpCodes::BAD_REQUEST);
	return request;
}

void HttpParser::parseCgiHeaders(HttpResponse& response, std::stringstream& data) {
	bool statusHeaderFound = false;
	bool hasContentType = false;
	// Busco y analizo los headers de respuesta de CGI
	std::string line;
	std::getline(data, line);
	while (line.length() > 0 && line[0] != HttpMessage::CR) {
		HttpHeader* head = HttpParser::parseHeader(line);
		std::string upHeadName = StrUtils::toUpper(head->getName());
		// Si recibí header de Status, tomo el valor y lo borro
		if (upHeadName == "STATUS") {
			statusHeaderFound = true;
			std::stringstream ss(head->getValue());
			delete head;  // Libero el header porque no se agrega a la rta
			int statusAux;
			ss >> statusAux;
			if(ss.good()) {
				response.setCode(statusAux);
				size_t posReason = ss.str().find_first_of(HttpMessage::SP);
				if (posReason != std::string::npos)
					response.setReasonPhrase(ss.str().substr(posReason));
			}
			else
				throw ParserException("Error de sintaxis en el status "
						"header del script", HttpCodes::INTERNAL_SERVER_ERROR);
		}
		else if (upHeadName == "CONTENT-TYPE") {
			hasContentType = true;
			response.addHeader(head);
		}
		else {
			// Agrego cualquier otro encabezado del script
			response.addHeader(head);
		}
		std::getline(data, line);
	}
	if(hasContentType) {
		// Si no envió status, asumo OK.
		if(!statusHeaderFound)
			response.setCode(HttpCodes::OK);
		// Levanto el body
		std::stringstream body;
		body << data.rdbuf();
		response.setBody(new HttpStringBody(body.str()));
	}
	else
		throw ParserException("Los encabezados devueltos del script no "
				"tienen Content-Type", HttpCodes::INTERNAL_SERVER_ERROR);
}
